using System.Collections.Generic;
using Godot;
using System;
using DelaunatorSharp;

namespace Box.Base {
	public partial class Voronoi : Node2D
	{
		[Export]
		public int PointNumber = 100;

		IPoint[] points;
		Delaunator delaunator;

		public Dictionary<long,VoronoiVertex> Vertices {get;} = new Dictionary<long, VoronoiVertex>();
		public Dictionary<long,VoronoiEdge> Edges {get;} = new Dictionary<long, VoronoiEdge>();
		public Dictionary<long,VoronoiCell> Cells {get;} = new Dictionary<long, VoronoiCell>();

		public Voronoi() {}
		public Voronoi(Vector2[] points) {
			delaunator = new  Delaunator(VectorArrToPointArr(points));

			delaunator.ForEachVoronoiCell(c => {
				VoronoiCell cell = new VoronoiCell();
				
				IPoint current_point;
				VoronoiVertex current;
				VoronoiVertex up = GetVertexOrCreate((int)c.Points[0].X,(int)c.Points[0].Y);
				VoronoiEdge edge;
				float cx = up.x;
				float cy = up.y;
				cell.Vertices.Add(up);
				for(int i = 1;i<c.Points.Length;i++) {
					current_point = c.Points[i];
					current = GetVertexOrCreate((int)current_point.X,(int)current_point.Y);

					edge = GetEdgeOrCreate(up,current);
					if(edge.C1 == null) {
						edge.C1 = cell;
					} else if(edge.C2 == null) {
						edge.C2 = cell;
					}
					
					cell.Edges.Add(edge);
					cell.Vertices.Add(current);

					up = current;
					cx += current.x;
					cy += current.y;
				}

				edge = GetEdgeOrCreate(up,GetVertexOrCreate((float)c.Points[0].X,(float)c.Points[0].Y));
				if(edge.C1 == null) {
					edge.C1 = cell;
				} else if(edge.C2 == null) {
					edge.C2 = cell;
				}
				cell.Edges.Add(edge);

				cx = cx / c.Points.Length;
				cy = cy / c.Points.Length;

				cell.IndexPoint = GetVertexOrCreate(cx,cy);
				long hash = cell.IndexPoint.GetHashValue();
				
				if(cell.Vertices.Count > 2) {
					Cells[hash] = cell;
				}
			});

			foreach(VoronoiCell cell in Cells.Values) {
				long hash = cell.IndexPoint.GetHashValue();
				foreach(VoronoiVertex vertex in cell.Vertices) {
					vertex.Cells[hash] = cell;
				}
				foreach(VoronoiEdge edge in cell.Edges) {
					VoronoiCell region_cell = edge.C1;
					if(region_cell == null || region_cell == cell) {
						region_cell = edge.C2;
					}
					if(region_cell == cell) {
						region_cell = null;
					}

					if(region_cell != null) {
						cell.Regions.Add(region_cell);
					}
				}
			}
		}

		protected VoronoiVertex GetVertexOrCreate(float x,float y) {
			long hash = VoronoiVertex.ToHashValue(x,y);
			if(!Vertices.ContainsKey(hash)) {
				Vertices[hash] = new VoronoiVertex(x,y);
			}
			return Vertices[hash];
		}

		protected VoronoiEdge GetEdgeOrCreate(VoronoiVertex p1,VoronoiVertex p2) {
			Vector2 c = (p1.Position + p2.Position) / 2;
			VoronoiVertex edge_index_point = GetVertexOrCreate(c.x,c.y);
			long hash = VoronoiVertex.ToHashValue(edge_index_point.x,edge_index_point.y);
			if(!Edges.ContainsKey(hash)) {
				VoronoiEdge edge = new VoronoiEdge();
				edge.P1 = p1;
				edge.P2 = p2;
				edge.IndexPoint = edge_index_point;
				Edges[hash] = edge;

				p1.Edges[hash] = edge;
				p2.Edges[hash] = edge;
			}
			return Edges[hash];
		}

		protected IPoint[] VectorArrToPointArr(Vector2[] points) {
			IPoint[] arr = new IPoint[points.Length];
			for(int i = 0;i<points.Length;i++) {
				arr[i] = new Point(points[i].x,points[i].y);
			}
			return arr;
		}

		protected Vector2[] PointArrToVectorArr(IPoint[] points) {
			Vector2[] arr = new Vector2[points.Length];
			for(int i = 0;i<points.Length;i++) {
				arr[i] = new Vector2((float)points[i].X,(float)points[i].Y);
			}
			return arr;
		}
	}

}
